// waitForChild.cpp : main project file.

#include "stdafx.h"
#include <vcclr.h>

using namespace System;

int doStuff();

int main(array<System::String ^> ^args)
{
    Console::WriteLine(L"Hello World");
	doStuff();
    return 0;
}

#pragma unmanaged
#define UNICODE
#define _UNICODE
#define STRICT


int doStuff()
{
	CHandle Job(CreateJobObject(nullptr, nullptr));
	if (!Job) {
		wprintf(L"CreateJobObject, error %d\n", GetLastError());
		return 0;
	}

	CHandle IOPort(CreateIoCompletionPort(INVALID_HANDLE_VALUE,
		nullptr, 0, 1));
	if (!IOPort) {
		wprintf(L"CreateIoCompletionPort, error %d\n",
			GetLastError());
		return 0;
	}

	JOBOBJECT_ASSOCIATE_COMPLETION_PORT Port;
	Port.CompletionKey = Job;
	Port.CompletionPort = IOPort;
	if (!SetInformationJobObject(Job,
		JobObjectAssociateCompletionPortInformation,
		&Port, sizeof(Port))) {
		wprintf(L"SetInformation, error %d\n", GetLastError());
		return 0;
	}
	
	JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedLimit;
	JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimit;
	basicLimit.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;

	extendedLimit.BasicLimitInformation = basicLimit;
	if (!SetInformationJobObject(Job,
		JobObjectExtendedLimitInformation,
		&extendedLimit, sizeof(extendedLimit))) {
		wprintf(L"SetInformation BasicLimitInformation, error %d\n", GetLastError());
		return 0;
	}


	PROCESS_INFORMATION ProcessInformation;
	STARTUPINFO StartupInfo = { sizeof(StartupInfo) };

	wchar_t *dsktp = L"WinSta0\\Default";
	StartupInfo.lpDesktop = dsktp;

	PWSTR CommandLine = PathGetArgs(GetCommandLine());

	HANDLE svctoken = NULL;
	HANDLE provtoken = NULL;
	DWORD error = 0;
	DWORD sessionId = 4;
	bool success = false;
	do
	{
		// Create token for new process by duplicating our own token
		if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &svctoken) == 0)
		{
			break;
		}
		DuplicateTokenEx(svctoken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &provtoken);
		CloseHandle(svctoken);
		svctoken = NULL;


		// Move the new token to the target session
		if (!SetTokenInformation(provtoken, TokenSessionId, &sessionId, sizeof(DWORD)))
		{
			wprintf(L"SetTokenInformation, error %d\n", error = GetLastError());
			return error;
		}

		// Set up the new process information
		if (!CreateProcessAsUser(provtoken, nullptr, CommandLine, nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &StartupInfo, &ProcessInformation))
		{
			wprintf(L"CreateProcessAsUser, error %d\n", error = GetLastError());
			return error;
		}
	
		success = true;
	} while (false);

	
	if (provtoken)
	{
		CloseHandle(provtoken);
	}

	if (!AssignProcessToJobObject(Job,
		ProcessInformation.hProcess)) {
		wprintf(L"Assign, error %d\n", GetLastError());
		return 0;
	}

	ResumeThread(ProcessInformation.hThread);
	CloseHandle(ProcessInformation.hThread);
	CloseHandle(ProcessInformation.hProcess);

	DWORD CompletionCode;
	ULONG_PTR CompletionKey;
	LPOVERLAPPED Overlapped;
	while (GetQueuedCompletionStatus(IOPort, &CompletionCode,
		&CompletionKey, &Overlapped, INFINITE) &&
		!((HANDLE)CompletionKey == Job &&
		CompletionCode == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)) {
		wprintf(L"Still waiting...\n");
	}

	wprintf(L"All done\n");

	return 0;
}
